<?php

namespace iBrand\Shop\Server\Controllers\V2;

use Carbon\Carbon;
use iBrand\Component\Balance\Repository\BalanceRepository;
use iBrand\Component\Discount\Applicators\DiscountApplicator;
use iBrand\Component\Discount\Models\Coupon;
use iBrand\Component\Discount\Models\Discount;
use iBrand\Component\Discount\Services\DiscountService;
use iBrand\Component\Goods\Models\GoodsProduct;
use iBrand\Component\Order\Models\Adjustment;
use iBrand\Component\Payment\Models\Payment;
use iBrand\Component\Point\Repository\PointRepository;
use iBrand\Component\Point\Applicator\PointApplicator;
use iBrand\Component\Order\Models\Order;
use iBrand\Component\Order\Models\OrderItem;
use iBrand\Component\Order\Processor\OrderProcessor;
use iBrand\Component\Order\Repositories\OrderRepository;
use iBrand\Component\Discount\Repositories\CouponRepository;
use iBrand\Component\Purchase\Models\StockRecord;
use iBrand\Component\Purchase\Repositories\StockRecordRepository;
use iBrand\Shop\Core\Models\Balance;
use iBrand\Shop\Core\Models\CashRecord;
use iBrand\Shop\Core\Models\O2oGoodsProducts;
use iBrand\Shop\Core\Models\PaymentDetail;
use iBrand\Shop\Core\Models\User;
use iBrand\Shop\Core\Repositories\CashRecordRepository;
use iBrand\Shop\Core\Repositories\ShopOrderRepository;
use iBrand\Shop\Core\Repositories\UserShopRepository;
use iBrand\Shop\Core\Models\O2oGoods;
use iBrand\Component\Goods\Models\SpecsValue;
use Illuminate\Http\Request;
use Validator;
use DB;

class ShoppingController extends Controller
{
    private $discountService;
    private $orderRepository;
    private $discountApplicator;
    private $couponRepository;
    private $orderProcessor;
    private $pointRepository;
    private $pointApplicator;
    private $userShopRepository;
    private $shopOrderRepository;
    private $balanceRepository;
    private $cashRecordRepository;
    private $stockRecordRepository;

    public function __construct(DiscountService $discountService
        , OrderRepository $orderRepository
        , CouponRepository $couponRepository
        , DiscountApplicator $discountApplicator
        , OrderProcessor $orderProcessor
        , PointRepository $pointRepository
        , PointApplicator $pointApplicator
        , UserShopRepository $userShopRepository
        , ShopOrderRepository $shopOrderRepository
        , BalanceRepository $balanceRepository
        , CashRecordRepository $cashRecordRepository
        , StockRecordRepository $stockRecordRepository
    )
    {
        $this->discountService = $discountService;
        $this->orderRepository = $orderRepository;
        $this->discountApplicator = $discountApplicator;
        $this->couponRepository = $couponRepository;
        $this->orderProcessor = $orderProcessor;
        $this->pointRepository = $pointRepository;
        $this->pointApplicator = $pointApplicator;
        $this->userShopRepository = $userShopRepository;
        $this->shopOrderRepository = $shopOrderRepository;
        $this->balanceRepository = $balanceRepository;
        $this->cashRecordRepository = $cashRecordRepository;
        $this->stockRecordRepository = $stockRecordRepository;
    }

    public function checkout(Request $request)
    {
        $input = $request->except('_token', 'file');
        $rules = [
            'user_id' => 'required|integer',
            'order_item.*.goods_id' => 'required',
            'order_item.*.user_discount' => 'required',
            'order_item.*.base_goods' => 'required|array',
            'order_item.*.base_product' => 'required|array',
            'order_item.*.num' => 'required|integer|min:1',
            'order_item.*.subtotal' => 'required|numeric|min:1',
            'order.coupon_id' => 'required|integer',
            'order.used_point' => 'required',
            'order.discount_amount' => 'required|integer|min:0',
            'order.coupon_amount' => 'required|integer|min:0',
            'order.point_amount' => 'required|integer|min:0',
            'order.total_discount_amount' => 'required|integer|min:0',
            'order.num' => 'required|integer|min:1',
            'order.total_amount' => 'required|integer|min:1',
            'order.pay_amount' => 'required|integer|min:1',
        ];
        $message = [
            "required" => ":attribute 不能为空",
            "integer" => ":attribute 必须为整数",
            "array" => ":attribute 不能为空",
            'order_item.*.num.min' => ":attribute 必须大于0",
            'order_item.*.subtotal.min' => ":attribute 必须大于0",
            'order.discount_amount.min' => ":attribute 必须大于等于0",
            'order.coupon_amount.min' => ":attribute 必须大于等于0",
            'order.point_amount.min' => ":attribute 必须大于等于0",
            'order.total_discount_amount.min' => ":attribute 必须大于等于0",
            'order.num.min' => ":attribute 必须大于0",
            'order.total_amount.min' => ":attribute 必须大于0",
            'order.pay_amount.min' => ":attribute 必须大于0",
        ];
        $attributes = [
            'user_id' => '用户id',
            'order_item.*.goods_id' => '商品信息',
            'order_item.*.base_goods' => '商品信息',
            'order_item.*.base_product' => '商品信息',
            'order_item.*.num' => '商品数量',
            'order_item.*.subtotal' => '商品金额',
            'order.coupon_id' => '优惠券信息',
            'order.discount_amount' => '订单折扣抵扣金额',
            'order.coupon_amount' => '订单优惠券抵扣金额',
            'order.point_amount' => '订单积分抵扣金额',
            'order.total_discount_amount' => '订单总优惠金额',
            'order.num' => '商品数量',
            'order.total_amount' => '订单总金额',
            'order.pay_amount' => '支付金额',
        ];

        $validator = Validator::make($input, $rules, $message, $attributes);
        if ($validator->fails()) {

            return $this->failed($validator->messages()->first());
        }

        $clerk = auth('clerk')->user();
        /*$userShop = $this->userShopRepository->findWhere(['user_id' => $input['user_id'], 'shop_id' => $clerk->shop_id, 'clerk_id' => $clerk->id])->first();*/

        /*$userShop = $this->userShopRepository->findWhere(['user_id' => $input['user_id']])->first();*/

        $user = User::find($input['user_id']);
        if (!$user) {
            return $this->api([], false, 500, '用户不存在');
        }

       
        if ($input['order']['used_point'] > 0 && $input['order']['point_amount'] > 0 AND !settings('order_use_point_enabled')) {
            return $this->api([], false, 500, '该商品不能使用积分');
        }

        $orderData = $input['order'];
        $orderItemsData = $input['order_item'];
        foreach ($orderItemsData as $item) {
            $o2oProduct = O2oGoodsProducts::where('id', $item['id'])->first();
            if (!$o2oProduct) {
                return $this->api([], false, 500, '商品信息错误');

                break;
            }

            if ($item['num'] > $o2oProduct->store_nums) {
                return $this->api([], false, 500, '商品：' . $item['base_goods']['name'] . ' ' . implode('-', $item['spec_text']) . '库存不足');

                break;
            }
        }

        $totalNum = array_sum(array_column($orderItemsData, 'num'));
        if ($totalNum != $orderData['num']) {
            return $this->api([], false, 500, '商品数量错误');
        }

        if ($orderData['pay_amount'] <= 0 || $orderData['pay_amount'] > $orderData['total_amount']) {
            return $this->api([], false, 500, '支付金额、优惠金额计算错误');
        }

        try {

            DB::beginTransaction();

            $order = new Order(['user_id' => $user->id, 'type' => Order::TYPE_SHOP, 'channel' => 'shop', 'channel_id' => $clerk->shop_id, 'submit_time' => Carbon::now()]);
            $order = $this->BuildOrderItems($orderItemsData, $order);

            //满件折
            if ($orderData['discount_id'] AND $discount = Discount::find($orderData['discount_id'])) {
                if ($this->discountService->checkDiscount($order, $discount)) {
                    $this->discountApplicator->apply($order, $discount);
                } else {
                    throw new \Exception('折扣信息有误，请确认后重试');
                }
            }

            //满减
            if ($orderData['discount_fix_id'] AND $discount = Discount::find($orderData['discount_fix_id'])) {
                if ($this->discountService->checkDiscount($order, $discount)) {
                    $this->discountApplicator->apply($order, $discount);
                } else {
                    throw new \Exception('优惠信息有误，请确认后重试');
                }
            }

            //优惠券抵扣
            if ($orderData['coupon_id'] AND $coupon = Coupon::find($orderData['coupon_id'])) {
                if ($coupon->used_at != null) {
                    throw new \Exception('此优惠券已被使用');
                }

                if ($user->can('update', $coupon) AND $this->discountService->checkCoupon($order, $coupon)) {
                    $this->discountApplicator->apply($order, $coupon);
                } else {
                    throw new \Exception('优惠券信息有误，请确认后重试');
                }
            }

            //积分抵扣
            if ($orderData['used_point'] > 0 && $orderData['point_amount'] > 0 && $orderData['point_amount'] AND settings('point_enabled')) {
                $orderPoint = $this->getOrderPoint($order, $user);

                if (empty($orderPoint) || abs($orderPoint['pointAmount']) < $orderData['point_amount']) {
                    throw new \Exception('积分抵扣金额错误');
                }

                $point = $orderData['used_point'];
                if ($this->pointRepository->checkUserPoint($order, $point)) {
                    $res = $this->pointApplicator->apply($order, $point);
                    if (!$res) {
                        throw new \Exception('积分处理出错');
                    }
                } else {
                    throw new \Exception('积分不足或不满足积分折扣规则');
                }

                $order->redeem_point = $orderData['used_point'];
            }


            $needPay = $order->getNeedPayAmount();
            \Log::info($needPay);
            \Log::info($orderData);
            \Log::info($order);
            if ($needPay != $orderData['pay_amount']) {
                throw new \Exception('支付金额错误');
            }

            if (!$order = $this->orderProcessor->create($order)) {
                throw new \Exception('订单提交失败，请确认后重试');
            }

            DB::commit();

            $balance = $this->balanceRepository->getSum($user->id);

            return $this->api(['order' => $order, 'user' => $user, 'balance' => $balance, 'needPay' => $needPay]);
        } catch (\Exception $exception) {
            DB::rollBack();

            \Log::info($exception->getMessage());
            \Log::info($exception->getTraceAsString());

            return $this->api([], false, 400, $exception->getMessage());
        }
    }

    public function getPointProportion()
    {
        $shop_id = request('shop_id');
        //全店禁用积分抵扣设置
        $pointSettings = settings('shop_point_disable');

        if ($pointSettings AND isset($pointSettings[$shop_id]) AND $pointSettings[$shop_id]) {
            return $this->success();
        }

        if ($pointSettings AND isset($pointSettings[$shop_id]) AND !$pointSettings[$shop_id] AND
            settings('order_use_point_enabled') AND settings('point_order_limit') AND
            settings('point_enabled') AND settings('point_proportion')
        ) {

            return $this->success([
                'point_order_limit' => settings('shop_point_order_limit'),
                'point_proportion' => settings('point_proportion'),
            ]);
        }

        return $this->success();
    }

    /**
     * @param $orderItems
     * @param $order
     *
     * @throws \Exception
     */
    private function BuildOrderItems($orderItems, $order)
    {
        $order_adjustments_total = 0;
        $adjustments = [];
        foreach ($orderItems as $item) {
            $item_meta = [
                'image' => $item['base_goods']['img'],
                'detail_id' => $item['base_goods']['id'],
                'dynamic_sku' => $item['base_product']['sku'],
                'specs_text' => implode('-', $item['spec_text']),
            ];

            $adjustments_total = 0;
            $o2oGoods = O2oGoods::where('goods_id', $item['base_goods']['id'])->where('shop_id', $order->channel_id)->first();
            $unit_price = $o2oGoods->goods->sell_price;
            if ($item['user_discount'] < 100) {
                $adjustments_total = (($unit_price * (100 - $item['user_discount']))) * $item['num'];
                //$order_adjustments_total += $adjustments_total;
                $adjustments[$item['base_product']['id']] = -$adjustments_total;

            }

            $orderItem = new OrderItem([
                'quantity' => $item['num'],
                'unit_price' => $unit_price,
                'item_id' => $item['base_product']['id'],
                'type' => GoodsProduct::class,
                'item_name' => $item['base_goods']['text'],
                'item_meta' => $item_meta,
                /*'item_discount'     => -$adjustments_total,
                'adjustments_total' => -$adjustments_total,*/
            ]);

            $orderItem->recalculateUnitsTotal();
            $orderItem->save();
            $order->addItem($orderItem);
        }

        foreach ($order->getItems() as $item) {
            if (isset($adjustments[$item->item_id])) {
                $adjustment = new Adjustment([
                    'order_id' => $order->id,
                    'order_item_id' => $item->id,
                    'type' => Adjustment::ORDER_ITEM_DISCOUNT_ADJUSTMENT,
                    'label' => '会员折扣',
                    'origin_type' => 'user_order_discount',
                    'origin_id' => $order->user_id,
                    'amount' => $adjustments[$item->item_id],
                ]);
                $order->addAdjustment($adjustment);

                $item->item_discount += $adjustments[$item->item_id];
                $item->recalculateAdjustmentsTotal();
            }
        }
        return $order;
    }

    public function getOrderPoint($order, $user)
    {
        $orderPoint = [];
        if ($this->getCanUsePoint($order->channel_id)) {
            $total = $this->getCanUsePointSum($order);
            $orderPoint = [
                'userPoint' => $this->pointRepository->getSumPointValid($user->id, 'default'),
                'pointToMoney' => settings('point_proportion') ? settings('point_proportion') : 0,
                'pointLimit' => settings('point_order_limit') / 100,
            ];

            $pointAmount = min($orderPoint['userPoint'] * $orderPoint['pointToMoney'], $total * $orderPoint['pointLimit']);
            $orderPoint['pointAmount'] = -$pointAmount;
            $orderPoint['pointCanUse'] = $pointAmount / $orderPoint['pointToMoney'];
            $orderPoint['pointCanotUseAmount'] = $this->getCanUsePointSum($order, false);
        }
        return $orderPoint;
    }

    private function getCanUsePoint($shop_id)
    {
        $pointSettings = settings('shop_point_disable');  //全店禁用积分抵扣设置

        if ($pointSettings && isset($pointSettings[$shop_id]) && $pointSettings[$shop_id]) {
            return false;
        }

        if ($pointSettings && isset($pointSettings[$shop_id]) && !$pointSettings[$shop_id] &&
            settings('order_use_point_enabled') && settings('point_order_limit') && settings('point_enabled') && settings('point_proportion')
        ) {
            return true;
        }

        return false;
    }

    /**
     * 获取能使用\不能使用积分抵扣的金额
     *
     * @param $order
     * @param $type
     *
     * @return mixed
     */
    private function getCanUsePointSum($order, $type = true)
    {
        $items = $order->items;
        $filters = $items->filter(function ($item, $key) use ($type, $order) {
            $goods = O2oGoods::where('goods_id', $item->item_meta['detail_id'])->where('shop_id', $order->channel_id)->first();
            if ($type) {
                return $goods->point_deduction;
            } else {
                return !$goods->point_deduction;
            }
        });

        return $filters->sum('total');
    }

    public function confirm(Request $request)
    {
        $input = $request->except('_token', 'file');
        $rules = [
            'user_id' => 'required',
            'clerk_id' => 'required',
            'order_no' => 'required',
            'actual' => 'required',//实收
            'remaining' => 'required',//找零
            'wipe' => 'required',//抹零
            'total_amount' => 'required',//总金额
            'payments' => 'required|array',
            'payments.*.amount' => 'required|integer',

        ];
        $message = [
            "required" => ":attribute 不能为空",
            "integer" => ":attribute 只能为整数",

        ];
        $attributes = [
            'user_id' => '用户id',
            'clerk_id' => '导购员',
            'order_no' => '订单编号',
            'payments' => '支付信息',
            'actual' => '实收金额',
            'remaining' => '找零金额',
            'wipe' => '抹零金额',
            'total_amount' => '总金额',
        ];

        $validator = Validator::make($input, $rules, $message, $attributes);
        if ($validator->fails()) {

            return $this->failed($validator->messages()->first());
        }

        $clerk = auth('clerk')->user();
        //, 'shop_id' => $clerk->shop_id
        /*$userShop = $this->userShopRepository->findWhere(['user_id' => $input['user_id']])->first();
        if (!$userShop) {
            return $this->failed('用户不存在');
        }*/

        $user = User::find($input['user_id']);
        if (!$user) {
            return $this->failed('用户不存在');
        }
        
        $order = $this->orderRepository->findWhere(['order_no' => $input['order_no']])->first();
        if (!$input['order_no'] || !$order) {
            return $this->failed('订单不存在');
        }

        if ($user->cant('submit', $order)) {
            return $this->failed('没有权限提交订单');
        }

        //应收总额 = 实收-找零
        if ($input['total_amount'] != ($input['actual'] - $input['remaining'])) {
            return $this->failed('实收金额、找零、抹零金额错误');
        }

        $sum = array_sum(array_column($input['payments'], 'amount'));
        if ($sum != $input['actual']) {
            return $this->failed('实收金额错误');
        }
        if ($sum != $input['total_amount'] + $input['remaining']) {
            return $this->failed('找零金额错误');
        }

        $order->note = $input['note'];
        try {

            DB::beginTransaction();

            $this->shopOrderRepository->create([
                'order_id' => $order->id,
                'clerk_id' => $input['clerk_id'],
                'shop_id' => $clerk->shop_id,
            ]);

            if ($input['wipe'] > 0) {
                $adjustment = new Adjustment([
                    'type' => Adjustment::ORDER_WIPE_DISCOUNT_ADJUSTMENT,
                    'label' => '抹零',
                    'origin_type' => 'wipe',
                    'origin_id' => $order->user_id,
                    'amount' => -$input['wipe'],
                ]);

                $order->addAdjustment($adjustment);
            }

            $needPay = $order->getNeedPayAmount();
            if ($input['total_amount'] != $needPay) {
                throw new \Exception('支付金额错误');
            }

            foreach ($order->getItems() as $item) {
                $o2oGoodsProduct = O2oGoodsProducts::where('goods_id', $item->item_meta['detail_id'])->where('shop_id', $clerk->shop_id)->where('product_id', $item->item_id)->first();
                if ($o2oGoodsProduct->store_nums < $item->quantity) {
                    throw new \Exception('商品：' . $item->item_name . ' ' . $item->item_meta['spec_text'] . '库存不足');
                    break;
                }

                $o2oGoodsProduct->store_nums = $o2oGoodsProduct->store_nums - $item->quantity;
                $o2oGoodsProduct->save();

                $o2oGoods = $o2oGoodsProduct->O2oGoods()->where('id', $o2oGoodsProduct->o2o_goods_id)->first();
                $o2oGoods->store_nums = $o2oGoods->store_nums - $item->quantity;
                $o2oGoods->save();

                $specID = $o2oGoodsProduct->baseProduct->specID;
                $spec0 = SpecsValue::find($specID[0]);
                $spec1 = SpecsValue::find($specID[1]);
                if ($spec0->spec_id == 1) {
                    $color = $spec1->name;
                    $color_id = $specID[1];
                    $size = $spec0->name;
                    $size_id = $specID[0];
                } elseif ($spec1->spec_id == 1) {
                    $color = $spec0->name;
                    $color_id = $specID[0];
                    $size = $spec1->name;
                    $size_id = $specID[1];
                }

                $this->stockRecordRepository->create([
                    'shop_id' => $clerk->shop_id,
                    'item_id' => $item->item_id,
                    'item_type' => GoodsProduct::class,
                    'type' => StockRecord::STORE_SALES,
                    'business_type' => Order::class,
                    'business_type_id' => $order->id,
                    'change_count' => -$item->quantity,
                    'current_stock' => $o2oGoodsProduct->store_nums,
                    'goods_id' => $o2oGoodsProduct->baseGoods->id,
                    'meta' => [
                        'goods_name' => $o2oGoodsProduct->baseGoods->name,
                        'goods_id' => $o2oGoodsProduct->baseGoods->id,
                        'goods_no' => $o2oGoodsProduct->baseGoods->goods_no,
                        'sku' => $o2oGoodsProduct->baseProduct->sku,
                        'color' => $color,
                        'color_id' => $color_id,
                        'size' => $size,
                        'size_id' => $size_id,
                    ],
                ]);
            }

            if ($order->redeem_point > 0) {
                $this->pointRepository->create([
                    'user_id' => $user->id,
                    'action' => 'order_discount',
                    'note' => '订单使用积分折扣',
                    'value' => (-1) * $order->redeem_point,
                    'valid_time' => 0,
                    'item_type' => 'iBrand\Component\Order\Models\Order',
                    'item_id' => $order->id,
                ]);

                event('point.change', $user->id);
            }

            foreach ($input['payments'] as $item) {
                if ($item['key'] == Payment::CHANNEL_BALANCE && $item['amount'] > 0) {
                    $balance = $this->balanceRepository->getSum($user->id);
                    if ($balance < $item['amount']) {
                        throw  new \Exception('余额不足');
                        break;
                    }
                }

                $amount = $item['amount'];
                if ($item['key'] == Payment::CHANNEL_CASH && $item['amount'] > 0) {
                    $this->cashRecordRepository->create([
                        'item_type' => Order::class,
                        'item_id' => $order->id,
                        'clerk_id' => $clerk->id,
                        'shop_id' => $clerk->shop_id,
                        'amount' => $item['amount'],
                        'category' => CashRecord::CATEGORY_ORDER,
                    ]);

                    $amount = $item['amount'] - $input['remaining']; //只有现金支付才有可能存在找零
                }

                $payment = new Payment([
                    'order_id' => $order->id,
                    'order_type' => Order::class,
                    'channel' => $item['key'],
                    'amount' => $amount,
                    'status' => Payment::STATUS_COMPLETED,
                    'paid_at' => Carbon::now(),
                ]);
                $order->payments()->save($payment);

                if ($item['key'] == Payment::CHANNEL_BALANCE && $item['amount'] > 0) {
                    $this->balanceRepository->addRecord([
                        'user_id' => $user->id,
                        'type' => Balance::TYPE_ORDER_PAYMENT,
                        'note' => '订单余额支付：' . $item['amount'] / 100 . ',订单号：' . $order->order_no,
                        'value' => -$item['amount'],
                        'current_balance' => $balance - $item['amount'],
                        'origin_id' => $payment->id,
                        'origin_type' => Payment::class,
                    ]);
                }
            }

            $order->pay_type = 'free';
            $order->status = Order::STATUS_PAY;
            $order->pay_time = Carbon::now();
            $order->pay_status = 1;
            $order->save();

            PaymentDetail::create([
                'order_id' => $order->id,
                'total_amount' => $input['total_amount'],   //总金额
                'wipe' => $input['wipe'],  //抹零
                'actual' => $input['actual'],  //实收
                'remaining' => $input['remaining'],  //找零
            ]);

            if ($input['remaining'] > 0) {   //找零金额纪录到现金流水表
                $this->cashRecordRepository->create([
                    'item_type' => Order::class,
                    'item_id' => $order->id,
                    'clerk_id' => $clerk->id,
                    'shop_id' => $clerk->shop_id,
                    'amount' => -$input['remaining'],
                    'category' => CashRecord::CATEGORY_REMAINING,
                ]);
            }


            event('o2o.order.onOrderCompleted', [$order]);

            DB::commit();

            return $this->api(['order' => $order]);
        } catch (\Exception $exception) {
            DB::rollBack();

            \Log::info($exception->getMessage() . $exception->getTraceAsString());

            return $this->failed($exception->getMessage());
        }
    }
}